Skip to main content

Polkadot Frontend DApp Template

React Vite Template with @bagpipes/wallet installed

Create a DApp for the Polkadot ecosystem. This DApp template provides a connection to the main Polkadot wallet extension providers out of the box, so you can get started building swiftly!

You can follow along below, or if you want to get started in seconds then you can just run the replit or fork our Frontend DApp Template on GitHub!

Run on Replit
  • Replit: Run project in seconds.

Code in a Replit Sandbox

  • Fork Github: Get started in minutes.
  • Follow along below: Setup project manually in ~12 minutes.

We will setup a Vite React project with @bagpipes/wallet (extension connection) installed!

Welcome to the "Hello World" DApp using @bagpipes/wallet. In this tutorial, we'll build a React application using Vite and integrate the Bagpipes Wallet for seamless wallet functionalities. Users can easily fork this template and start building their applications with wallet functionalities already set up.

Table of Contents

  1. Prerequisites
  2. Project Setup
  3. Installing Dependencies
  4. Project Structure
  5. Using @bagpipes/wallet
    • Setting Up the Wallet Context Provider
    • Creating the Wallet Integration Components
  6. Running the Project
  7. Conclusion

Prerequisites

  • Node.js (v18)
  • npm or Yarn
  • Git (optional, for version control and hosting on GitHub)

Project Setup

1. Create a New Vite React Project

We'll use Vite to set up a fast and lightweight React project.

yarn create vite bagpipes-wallet-template --template react

Navigate to the project directory:

cd bagpipes-wallet-template

2. Initialize Git (Optional)

If you plan to host this template on GitHub or another Git repository, initialize Git:

git init

Installing Dependencies

1. Install Project Dependencies

First, install the basic dependencies:

npm install

2. Install @bagpipes/wallet and Other Necessary Packages

npm install @bagpipes/wallet antd @polkadot/util-crypto react-router-dom @ant-design/icons
  • @bagpipes/wallet: The wallet integration library.
  • antd: UI components library used by @bagpipes/wallet.
  • @polkadot/util-crypto: Utility functions for working with addresses.
  • react-router-dom: For routing in the React app.

Project Structure

Your project structure should look like this:

bagpipes-wallet-template/
├── node_modules/
├── public/
├── src/
│ ├── App.css
│ ├── App.jsx
│ ├── assets/
│ │ └── react.svg
│ ├── components/
│ │ ├── AccountInfo.jsx
│ │ ├── ConnectWalletButton.jsx
│ │ ├── Header.jsx
│ │ └── WalletWidgetWrapper.jsx
│ ├── contexts/
│ │ └── ThemeContext.jsx
│ ├── index.css
│ ├── main.jsx
│ └── styles/
│ ├── AccountInfo.css
│ ├── ConnectWalletButton.scss
│ ├── Header.css
│ └── WalletWidgetWrapper.css
├── package.json
├── vite.config.js
└── README.md

Create Directories and Files

Create the necessary directories and files:

# Navigate to the project root directory

# Create components, contexts, and styles directories
mkdir -p src/components src/contexts src/styles src/assets

# Create component files
touch src/components/AccountInfo.jsx src/components/ConnectWalletButton.jsx src/components/WalletWidgetWrapper.jsx src/components/Header.jsx

# Create context file
touch src/contexts/ThemeContext.jsx

# Create style files
touch src/styles/AccountInfo.css src/styles/ConnectWalletButton.scss src/styles/WalletWidgetWrapper.css src/styles/Header.css

# If you haven't already, create App.css and index.css
touch src/App.css src/index.css

Our DApp template will have a Wallet widget in the top right corner of the screen that your users can connect with their extension.

Using @bagpipes/wallet

Setting Up the Wallet Context Provider

Wrap your application with the WalletContextProvider to supply wallet context values. Update your App.jsx file as follows:

// src/App.jsx
import { useContext } from 'react';
import {
WalletContextProvider,
WalletContext,
SelectWalletModal,
} from '@bagpipes/wallet';
import ConnectWalletButton from './components/ConnectWalletButton';
import AccountInfo from './components/AccountInfo';
import WalletWidgetWrapper from './components/WalletWidgetWrapper';
import Header from './components/Header';
import { Routes, Route, Outlet } from 'react-router-dom';
import './App.css';
import '@bagpipes/wallet/dist/esm/wallet.css';

function Chains() {
return <h2>Chains Page</h2>;
}

function Home() {
return <h2>Home Page</h2>;
}

function Lab() {
return <h2>Lab Page</h2>;
}

function App() {
const walletContext = useContext(WalletContext);

return (
<WalletContextProvider>
<Header open={!!(walletContext.wallet || walletContext.evmWallet)} />
<div className="app-header">
<WalletWidgetWrapper />
</div>
<div className="app-content">
<h1>Hello, Bagpipes Wallet!</h1>
<ConnectWalletButton />
{/* <AccountInfo /> */}
</div>
<main>
<Routes>
<Route
path="/"
element={<h1>Welcome to Bagpipes Wallet DApp</h1>}
/>
<Route path="/chains" element={<Chains />} />
<Route path="/home" element={<Home />} />
<Route path="/lab" element={<Lab />} />
<Route path="/wallet-info" element={<AccountInfo />} />
</Routes>
<Outlet />
</main>
<SelectWalletModal theme="light" debug={false} />
</WalletContextProvider>
);
}

export default App;
  • We import useContext from React and WalletContext from @bagpipes/wallet to access wallet context values.
  • We wrap our application with WalletContextProvider.
  • We include the Header component, which we'll define later.
  • We set up routing using react-router-dom.

Update main.jsx

Update your main.jsx file to include HashRouter for routing and import Ant Design reset styles:

// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { HashRouter } from 'react-router-dom';
import App from './App';
import 'antd/dist/reset.css';

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>
);

Creating the Wallet Integration Components

1. Create the ConnectWalletButton Component

// src/components/ConnectWalletButton.jsx
import { useContext } from 'react';
import { OpenSelectWallet } from '@bagpipes/wallet';
import { Button } from 'antd';

function ConnectWalletButton() {
const selectWallet = useContext(OpenSelectWallet);

const handleConnectWallet = () => {
selectWallet.open();
};

return (
<Button className="connect-wallet-button" onClick={handleConnectWallet}>
Connect Wallet
</Button>
);
}

export default ConnectWalletButton;
  • We use Ant Design's Button component for styling.
  • We use OpenSelectWallet to open the wallet selection modal.

2. Create the AccountInfo Component

// src/components/AccountInfo.jsx
import { useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { WalletContext } from '@bagpipes/wallet';
import '../styles/AccountInfo.css';

function AccountInfo() {
const walletContext = useContext(WalletContext);
const navigate = useNavigate();

const walletConnected = walletContext.wallet || walletContext.evmWallet;
const wallet = walletContext.wallet || walletContext.evmWallet;

const handleDisconnect = () => {
walletContext.disconnectWallet();
navigate('/');
};

if (!walletConnected) {
return <div className="account-info">No wallet connected</div>;
}

return (
<div className="account-info">
<h2>Wallet Information</h2>
<div className="wallet-summary">
<div className="wallet-details">
<div className="wallet-title">
<strong>Wallet:</strong> {wallet.title}
<img
className="wallet-logo"
src={wallet.logo.src}
alt={wallet.logo.alt}
/>
</div>
<div className="wallet-version">
<strong>Version:</strong>{' '}
{walletContext.wallet?.extension?.version ||
walletContext.evmWallet?.version ||
'Unknown'}
</div>
</div>
</div>
<div className="accounts">
<h3>Connected Accounts</h3>
{walletContext.accounts.map((account, index) => (
<div key={index} className="account-card">
<div className="account-details">
<strong>{account.name || 'Unnamed Account'}</strong>
<p>{account.address}</p>
</div>
</div>
))}
</div>
<button className="disconnect-btn" onClick={handleDisconnect}>
Disconnect
</button>
</div>
);
}

export default AccountInfo;
  • Displays connected wallet and account information.
  • Allows the user to disconnect the wallet.

3. Create the WalletWidgetWrapper Component

// src/components/WalletWidgetWrapper.jsx
import { WalletWidget } from '@bagpipes/wallet';
import '../styles/WalletWidgetWrapper.css';

function WalletWidgetWrapper() {
return (
<div className="wallet-widget-wrapper">
<WalletWidget />
</div>
);
}

export default WalletWidgetWrapper;
  • Wraps the WalletWidget from @bagpipes/wallet.

4. Create the Header Component

// src/components/Header.jsx
import React, { useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import {
OpenSelectWallet,
WalletContext,
WalletIcon,
WalletWidget,
} from '@bagpipes/wallet';
import '../styles/Header.css';

function Header({ open }) {
const navigate = useNavigate();
const walletContext = useContext(WalletContext);
const selectWallet = useContext(OpenSelectWallet);
const wallet = walletContext.wallet || walletContext.evmWallet;

const handleBuilder = () => navigate('/builder');
const handleChains = () => navigate('/chains');
const handleLab = () => navigate('/lab');

if (!open) {
return null;
}

return (
<header className="header">
<div className="header-content">
<img src="/logo.svg" alt="Bagpipes Logo" className="header-logo" />
<nav className="header-nav">
<button onClick={handleBuilder} className="header-btn">
Builder
</button>
<button onClick={handleChains} className="header-btn">
Chains
</button>
<button onClick={handleLab} className="header-btn">
Lab
</button>
</nav>
<button onClick={selectWallet.open} className="header-wallet-btn">
<WalletIcon /> {wallet ? 'Wallet Connected' : 'Connect Wallet'}
</button>
</div>
<WalletWidget />
</header>
);
}

export default Header;
  • A header component that includes navigation and wallet connection status.

Adding Styles

Add the provided CSS and SCSS files to src/styles/.

Header.css

/* src/styles/Header.css */

.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: #f8f9fa;
border-bottom: 1px solid #ddd;
}

.header-logo {
height: 40px;
}

.header-nav {
display: flex;
gap: 1rem;
}

.header-btn {
background: none;
border: none;
color: #0070f3;
font-size: 1rem;
cursor: pointer;
}

.header-btn:hover {
text-decoration: underline;
}

.header-wallet-btn {
display: flex;
align-items: center;
background: #0070f3;
color: white;
border: none;
border-radius: 4px;
padding: 0.5rem 1rem;
cursor: pointer;
}

.header-wallet-btn svg {
margin-right: 0.5rem;
}

.header-wallet-btn:hover {
background: #005bb5;
}

AccountInfo.css

/* src/styles/AccountInfo.css */

.account-info {
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
max-width: 1000px;
margin: 1rem auto;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.account-info h2,
.account-info h3 {
margin-bottom: 1rem;
color: #333;
}

.wallet-type {
margin-bottom: 1rem;
}

.accounts {
margin-bottom: 1rem;
}

.account-card {
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 0.5rem;
cursor: pointer;
background: #fff;
transition: background 0.3s ease, border-color 0.3s ease;
}

.account-card:hover {
background: #f0f0f0;
border-color: #0070f3;
}

.account-card.selected {
background: #e6f7ff;
border-color: #0070f3;
}

.account-details {
display: flex;
justify-content: space-between;
align-items: center;
}

.account-name {
font-weight: bold;
color: #333;
}

.account-address {
font-size: 0.9rem;
color: #555;
display: flex;
align-items: center;
}

.copy-btn {
margin-left: 0.5rem;
padding: 0.25rem 0.5rem;
font-size: 0.8rem;
background: #0070f3;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

.copy-btn:hover {
background: #005bb5;
}

.selected-account-details {
margin-top: 1rem;
padding: 1rem;
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
}

.wallet-title {
display: flex;
align-items: center;
}

.wallet-logo {
margin-left: 5px;
}

Update Other Styles as Necessary

Similarly, add or update styles for ConnectWalletButton.scss, WalletWidgetWrapper.css, etc.

Running the Project

1. Start the Development Server

npm run dev

This will start the Vite development server, and you can view your app at http://localhost:5173 (default port).

2. Test the Wallet Integration

  • Click on the "Connect Wallet" button.
  • The SelectWalletModal should appear.
  • Select a wallet to connect.
  • After connecting, the AccountInfo component should display the connected wallet type and account address.
  • The WalletWidget should display wallet information in the header.

Here we have a connected wallet extension with selected accounts shown.

Conclusion

You have now successfully set up a basic DApp using @bagpipes/wallet. This template can be forked and extended for your own projects, with wallet functionalities already integrated.